package weixin

import (
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"go.uber.org/zap"
	"golang.org/x/net/context"
	"io/ioutil"
	Rand "math/rand"
	log "maya-service/collector/logger"
	"maya-service/collector/trace_log"
	"maya-service/config"
	"maya-service/constant"
	"maya-service/errors"
	"maya-service/event"
	"maya-service/lib/json_callback/wxbizjsonmsgcrypt"
	"maya-service/lib/json_callback/wxbizmsgcrypt"
	modelEvent "maya-service/model/event"
	"maya-service/utils"
	"maya-service/utils/request"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

var (
	cursorMap = map[string]string{}
)

type AccountList struct {
	OpenKfid        string `json:"open_kfid"`
	Name            string `json:"name"`
	ManagePrivilege bool   `json:"manage_privilege"`
}

func BatchSendMsg(orderNo, externalUserId string, openKfId string, count int, status int) {
	SendMessage(orderNo, externalUserId, openKfId, "正在通知咨询师抢单", status)
	time.Sleep(1 * time.Second)
	SendMessage(orderNo, externalUserId, openKfId, fmt.Sprintf("已通知%v个咨询师....", count), status)
	time.Sleep(1 * time.Second)
	SendMessage(orderNo, externalUserId, openKfId, "预计1min内接单", status)
	time.Sleep(1 * time.Second)
	SendMessage(orderNo, externalUserId, openKfId, "1min 后为您自动转接资深咨询师", status)
	return
}

// SendWelcomeMessage 发送消息
func SendWelcomeMessage(welcomeCode string, orderNo string) error {
	type ClickItem struct {
		Id      string `json:"id"`
		Content string `json:"content"`
	}
	type MenuItem struct {
		Type  string    `json:"type"`
		Click ClickItem `json:"click"`
	}
	type MsgMenu struct {
		HeadContent string     `json:"head_content"`
		List        []MenuItem `json:"list"`
	}
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg_on_event?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		Code    string  `json:"code"`
		Msgid   string  `json:"msgid"`
		Msgtype string  `json:"msgtype"`
		Msgmenu MsgMenu `json:"msgmenu"`
	}{
		Code:    welcomeCode,
		Msgid:   fmt.Sprintf("%v", time.Now().UnixNano()),
		Msgtype: "msgmenu",
		Msgmenu: MsgMenu{
			HeadContent: "您是否同意本次派单服务呢？",
			List: []MenuItem{
				{
					Type: "click",
					Click: struct {
						Id      string `json:"id"`
						Content string `json:"content"`
					}{
						Id:      fmt.Sprintf("%v_%v", "101", orderNo),
						Content: "同意",
					},
				},
				{
					Type: "click",
					Click: struct {
						Id      string `json:"id"`
						Content string `json:"content"`
					}{
						Id:      fmt.Sprintf("%v_%v", "102", orderNo),
						Content: "拒绝",
					},
				},
			},
		},
	}), nil)

	trace_log.DebugLogger(orderNo, "####### 欢迎确认消息 SendWelcomeMessage: rsp:", rsp, " err:", err)
	if err != nil {
		return err
	}

	type Response struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)
	if ret.ErrCode != 0 {
		return fmt.Errorf("errcode is not zero")
	}
	return nil
}

// SendMessageWithMenu 发送菜单信息
func SendMessageWithMenu(externalUserId string, openKfId string, orderNo string) error {
	type ClickItem struct {
		Id      string `json:"id"`
		Content string `json:"content"`
	}
	type MenuItem struct {
		Type  string    `json:"type"`
		Click ClickItem `json:"click"`
	}
	type MsgMenu struct {
		HeadContent string     `json:"head_content"`
		List        []MenuItem `json:"list"`
	}

	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		Touser   string  `json:"touser"`
		OpenKfid string  `json:"open_kfid"`
		Msgtype  string  `json:"msgtype"`
		Msgmenu  MsgMenu `json:"msgmenu"`
	}{
		Touser:   externalUserId,
		OpenKfid: openKfId,
		Msgtype:  "msgmenu",
		Msgmenu: MsgMenu{
			HeadContent: "您是否同意本次派单服务呢？",
			List: []MenuItem{
				{
					Type: "click",
					Click: struct {
						Id      string `json:"id"`
						Content string `json:"content"`
					}{
						Id:      fmt.Sprintf("%v_%v", "101", orderNo),
						Content: "同意",
					},
				},
				{
					Type: "click",
					Click: struct {
						Id      string `json:"id"`
						Content string `json:"content"`
					}{
						Id:      fmt.Sprintf("%v_%v", "102", orderNo),
						Content: "拒绝",
					},
				},
			},
		},
	}), nil)
	trace_log.DebugLogger(orderNo, "######## 确认消息 SendMessageWithMenu rsp:", rsp, " err:", err)
	if err != nil {
		return err
	}

	type Response struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
		Url     string `json:"url"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)
	return nil
}

// SendMessage 发送消息
func SendMessage(orderNo, externalUserId string, openKfId string, message string, status int) error {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		Touser   string `json:"touser"`
		OpenKfid string `json:"open_kfid"`
		Msgtype  string `json:"msgtype"`
		Text     struct {
			Content string `json:"content"`
		} `json:"text"`
	}{
		Touser:   externalUserId,
		OpenKfid: openKfId,
		Msgtype:  "text",
		Text: struct {
			Content string `json:"content"`
		}{
			Content: message,
		},
	}), nil)
	trace_log.DebugLogger(orderNo, "######## SendMessage SendMessageWithMenu status", status, " rsp:", rsp, " err:", err)
	if err != nil {
		return err
	}

	type Response struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
		Url     string `json:"url"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)
	return nil
}

func GetLinkUrl(kfId string) string {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/add_contact_way?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		OpenKfid string `json:"open_kfid"`
	}{
		OpenKfid: kfId,
	}), nil)
	fmt.Println(rsp, err)
	if err != nil {
		return ""
	}

	type Response struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
		Url     string `json:"url"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)
	return ret.Url
}

// CacheKfLinkList 获取客服账账号列表
func CacheKfLinkList() (list []*AccountList) {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/account/list?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		Offset int `json:"offset"`
		Limit  int `json:"limit"`
	}{
		Offset: 0,
		Limit:  100,
	}), nil)
	fmt.Println(rsp, err)
	if err != nil {
		return nil
	}

	// 响应结构体
	type Response struct {
		ErrCode     int            `json:"errcode"`
		ErrMsg      string         `json:"errmsg"`
		AccountList []*AccountList `json:"account_list"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)

	if ret.ErrCode != 0 {
		log.Error("GetKfLinkList err ", zap.Any("err", ret.ErrMsg), zap.Any("rsp", rsp))
		return nil
	}

	for _, v := range ret.AccountList {
		list = append(list, v)
	}

	log.Info("########## 获取客服链接 GetKfLinkList", zap.Any("rsp", rsp))

	if len(list) > 0 {
		for _, v := range list {
			if (strings.Contains(v.Name, "金牌督导师") || strings.Contains(v.Name, "专家督导师")) && v.ManagePrivilege {
				list = append(list, v)
				urlName := GetLinkUrl(v.OpenKfid)
				constant.KfIdMap[v.OpenKfid] = &constant.KfConfig{
					Url:   urlName,
					Level: 3,
				}
				log.Info("########## 金牌督导师", zap.Any("openKfid", v.OpenKfid), zap.Any("url", urlName))
			}

			if (strings.Contains(v.Name, "资深督导师") || strings.Contains(v.Name, "中级督导师")) && v.ManagePrivilege {
				list = append(list, v)
				urlName := GetLinkUrl(v.OpenKfid)
				constant.KfIdMap[v.OpenKfid] = &constant.KfConfig{
					Url:   urlName,
					Level: 2,
				}
				log.Info("########## 资深督导师", zap.Any("openKfid", v.OpenKfid), zap.Any("url", urlName))
			}

			if (strings.Contains(v.Name, "人气督导师") || strings.Contains(v.Name, "初级督导师")) && v.ManagePrivilege {
				list = append(list, v)
				urlName := GetLinkUrl(v.OpenKfid)
				constant.KfIdMap[v.OpenKfid] = &constant.KfConfig{
					Url:   urlName,
					Level: 1,
				}
				log.Info("########## 人气督导师", zap.Any("openKfid", v.OpenKfid), zap.Any("url", urlName))
			}
		}
	}

	return nil
}

// GetAccountListByKfId 查询客服成员列表
func GetAccountListByKfId(kfId string) (userIds []string, err error) {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/servicer/list?access_token=%v&open_kfid=%v", accessToken, kfId)
	rsp, err := request.DoRequest("POST", url, "", nil)
	fmt.Println(rsp, err)
	if err != nil {
		return
	}

	type Servicer struct {
		UserId string `json:"userid"`
		Status int    `json:"status"`
	}
	// 响应结构体
	type Response struct {
		ErrCode      int         `json:"errcode"`
		ErrMsg       string      `json:"errmsg"`
		ServicerList []*Servicer `json:"servicer_list"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)

	if ret.ErrCode != 0 {
		log.Error("AddKfAccount err ", zap.Any("err", ret.ErrMsg), zap.Any("rsp", rsp), zap.Any("kfId", kfId))
		return
	}

	for _, v := range ret.ServicerList {
		if v.Status != 0 {
			continue
		}
		userIds = append(userIds, v.UserId)
	}

	log.Info("######### GetAccountListByKfId  ", zap.Any("kfId", kfId), zap.Any("userIds", userIds))
	return
}

// AddKfAccount 添加客服人员
func AddKfAccount(kfId string, userList []string) error {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/servicer/add?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		OpenKfid   string   `json:"open_kfid"`
		UseridList []string `json:"userid_list"`
	}{
		OpenKfid:   kfId,
		UseridList: userList,
	}), nil)
	fmt.Println(rsp, err)
	if err != nil {
		return err
	}

	// 响应结构体
	type Response struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)

	if ret.ErrCode != 0 {
		log.Error("AddKfAccount err ", zap.Any("err", ret.ErrMsg), zap.Any("rsp", rsp), zap.Any("kfId", kfId), zap.Any("userList", utils.ToJsonString(userList)))
		return fmt.Errorf("请求失败")
	}
	return nil
}

// RemoveKfAccount 删除客服人员
func RemoveKfAccount(kfId string, userList []string) error {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/servicer/del?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		OpenKfid   string   `json:"open_kfid"`
		UseridList []string `json:"userid_list"`
	}{
		OpenKfid:   kfId,
		UseridList: userList,
	}), nil)
	fmt.Println(rsp, err)
	if err != nil {
		return err
	}

	// 响应结构体
	type Response struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)

	if ret.ErrCode != 0 {
		log.Error("RemoveKfAccount err ", zap.Any("err", ret.ErrMsg), zap.Any("rsp", rsp), zap.Any("kfId", kfId), zap.Any("userList", utils.ToJsonString(userList)))
		return fmt.Errorf("请求失败")
	}
	return nil
}

// RefreshCursor cursor
func RefreshCursor() {
	CacheKfLinkList()
	for k, _ := range constant.KfIdMap {
		cursor, _ := ReadMessage(k, "", 1000, true)
		if cursor != "" {
			cursorMap[k] = cursor
		}
	}
}

func GenerateNonceStr(length int) string {
	const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	Rand.Seed(time.Now().UnixNano())

	var result strings.Builder
	for i := 0; i < length; i++ {
		result.WriteByte(chars[Rand.Intn(len(chars))])
	}

	return result.String()
}

func GetSignature(url string) (string, int64, string) {
	noncestr := GenerateNonceStr(16)
	jsapi_ticket, _ := GetJsapiTicket()
	timestamp := time.Now().Unix()
	JSAPITICKET := fmt.Sprintf("jsapi_ticket=%v&noncestr=%v&timestamp=%v&url=%v", jsapi_ticket, noncestr, timestamp, url)
	hasher := sha1.New()

	// 将字符串转换为字节数组，并写入哈希对象
	hasher.Write([]byte(JSAPITICKET))

	// 获取 SHA-1 签名（哈希值）
	signature := hasher.Sum(nil)

	// 将字节数组转为十六进制字符串
	return noncestr, timestamp, hex.EncodeToString(signature)
}

func GetJsapiTicket() (string, error) {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=%v&type=agent_config", accessToken)
	rsp, err := request.DoRequest("GET", url, "", nil)
	fmt.Println(rsp, err)
	if err != nil {
		return "", err
	}

	// 响应结构体
	type Response struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
		Ticket  string `json:"ticket"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)

	if ret.ErrCode != 0 {
		return ret.ErrMsg, fmt.Errorf("请求失败")
	}
	return ret.Ticket, nil
}

func GetServiceState(openKfid string, externalUserid string) int {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/get?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		OpenKfid       string `json:"open_kfid"`
		ExternalUserid string `json:"external_userid"`
	}{
		OpenKfid:       openKfid,
		ExternalUserid: externalUserid,
	}), nil)

	if err != nil {
		return -1
	}

	// 响应结构体
	type Response struct {
		ErrCode      int    `json:"errcode"`
		ErrMsg       string `json:"errmsg"`
		ServiceState int    `json:"service_state"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)

	if ret.ErrCode != 0 {
		return -1
	}
	return ret.ServiceState
}

// GetKfAccountList 查询客服账号列表
func GetKfAccountList() {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/account/list?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		Offset int `json:"offset"`
		Limit  int `json:"limit"`
	}{
		Offset: 0,
		Limit:  1000,
	}), nil)
	fmt.Println(rsp, err)
	return
}

// GetUserBaseInfo 获取客户基础信息
func GetUserBaseInfo(externalUseridList string) (unionId string) {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/customer/batchget?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		ExternalUseridList []string `json:"external_userid_list"`
	}{
		ExternalUseridList: []string{externalUseridList},
	}), nil)
	if err != nil {
		return ""
	}

	type Customer struct {
		ExternalUserID string `json:"external_userid"`
		Nickname       string `json:"nickname"`
		Avatar         string `json:"avatar"`
		Gender         int    `json:"gender"`
		UnionID        string `json:"unionid"`
	}

	// 响应结构体
	type Response struct {
		ErrCode            int        `json:"errcode"`
		ErrMsg             string     `json:"errmsg"`
		CustomerList       []Customer `json:"customer_list"`
		InvalidExternalIDs []string   `json:"invalid_external_userid"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)

	if ret.ErrCode != 0 {
		return ""
	}

	if len(ret.CustomerList) > 0 {
		return ret.CustomerList[0].UnionID
	}
	return
}

func ReadMessage(openKfid string, token string, limit int, isCalcCursor bool) (cursor string, externalUserid string) {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/sync_msg?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		OpenKfid string `json:"open_kfid"`
		Token    string `json:"token"`
		Limit    int    `json:"limit"`
	}{
		OpenKfid: openKfid,
		Token:    token,
		Limit:    limit,
	}), nil)

	if err != nil {
		return "", ""
	}

	// 外部消息体结构
	type EventMessage struct {
		EventType    string `json:"event_type"`
		Scene        string `json:"scene"`
		SceneParam   string `json:"scene_param"`
		OpenKfid     string `json:"open_kfid"`
		ExternalUser string `json:"external_userid"`
	}

	// 单条消息结构
	type Message struct {
		MsgID    string       `json:"msgid"`
		SendTime int64        `json:"send_time"`
		Origin   int          `json:"origin"`
		MsgType  string       `json:"msgtype"`
		Event    EventMessage `json:"event"`
	}

	// 根结构体
	type Response struct {
		ErrCode    int       `json:"errcode"`
		ErrMsg     string    `json:"errmsg"`
		NextCursor string    `json:"next_cursor"`
		MsgList    []Message `json:"msg_list"`
		HasMore    int       `json:"has_more"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)

	if ret.ErrCode != 0 {
		return "", ""
	}

	if isCalcCursor {
		if ret.HasMore == 0 {
			return ret.NextCursor, ""
		}
		ReadMessage(openKfid, token, limit, isCalcCursor)
	}

	if len(ret.MsgList) > 0 && ret.MsgList[0].Event.EventType == "enter_session" && openKfid == ret.MsgList[0].Event.OpenKfid {
		return "", ret.MsgList[0].Event.ExternalUser
	}

	return "", ""
}

// ReadUserClickMessage 读取消息
func ReadUserClickMessage(message string) (isReject bool, orderNo string, externalUserId string) {
	// 外部消息体结构
	type Text struct {
		Content string `json:"content"`
		MenuId  string `json:"menu_id"`
	}

	// 单条消息结构
	type Message struct {
		MsgID          string `json:"msgid"`
		SendTime       int64  `json:"send_time"`
		Origin         int    `json:"origin"`
		MsgType        string `json:"msgtype"`
		ExternalUserid string `json:"external_userid"`
		Text           Text   `json:"text"`
	}

	// 根结构体
	type Response struct {
		ErrCode    int       `json:"errcode"`
		ErrMsg     string    `json:"errmsg"`
		NextCursor string    `json:"next_cursor"`
		MsgList    []Message `json:"msg_list"`
		HasMore    int       `json:"has_more"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(message), ret)

	if ret.ErrCode != 0 {
		return true, "-1", ""
	}

	sort.Slice(ret.MsgList, func(i, j int) bool {
		return ret.MsgList[i].SendTime > ret.MsgList[j].SendTime
	})

	for _, v := range ret.MsgList {
		ids := strings.Split(v.Text.MenuId, "_")
		if len(ids) != 2 {
			return true, "-1", ""
		}
		orderNo = ids[1]
		menuId := ids[0]
		if v.MsgType == "text" {
			if menuId == "102" {
				return true, orderNo, v.ExternalUserid
			}

			if menuId == "101" {
				return false, orderNo, v.ExternalUserid
			}

			return true, "-1", v.ExternalUserid
		}
	}

	return true, "-1", ""
}

func ReadMessageFromKfLink(token string, openKfid string) (rsp string, msgType string, err error) {
	nextCursor := ""
	if v, ok := cursorMap[openKfid]; ok {
		nextCursor = v
	}
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/sync_msg?access_token=%v", accessToken)
	rsp, err = request.DoRequest("POST", url, utils.ToJsonString(&struct {
		OpenKfid string `json:"open_kfid"`
		Token    string `json:"token"`
		Limit    int    `json:"limit"`
		Cursor   string `json:"cursor"`
	}{
		OpenKfid: openKfid,
		Token:    token,
		Limit:    50,
		Cursor:   nextCursor,
	}), nil)

	log.Info("################ 读取对话消息 ReadMessageFromKfLink", zap.Any("rsp", rsp), zap.Any("err", err))
	if err != nil {
		return "", "", err
	}

	// 单条消息结构
	type Message struct {
		MsgID          string `json:"msgid"`
		SendTime       int64  `json:"send_time"`
		Origin         int    `json:"origin"`
		MsgType        string `json:"msgtype"`
		ExternalUserid string `json:"external_userid"`
	}

	// 根结构体
	type Response struct {
		ErrCode    int       `json:"errcode"`
		ErrMsg     string    `json:"errmsg"`
		NextCursor string    `json:"next_cursor"`
		MsgList    []Message `json:"msg_list"`
		HasMore    int       `json:"has_more"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(rsp), ret)
	cursorMap[openKfid] = ret.NextCursor
	if len(ret.MsgList) == 0 {
		return "", "", errors.New("消息为空")
	}
	return
}

// ReadExternUserIdFromMessage 读取消息
func ReadExternUserIdFromMessage(message, openKfid string) (externalUserId string, isSkip bool, welComeCode string, msgId string) {

	// 外部消息体结构
	type EventMessage struct {
		EventType    string `json:"event_type"`
		Scene        string `json:"scene"`
		SceneParam   string `json:"scene_param"`
		OpenKfid     string `json:"open_kfid"`
		ExternalUser string `json:"external_userid"`
		WelcomeCode  string `json:"welcome_code"`
	}

	// 单条消息结构
	type Message struct {
		MsgID        string       `json:"msgid"`
		SendTime     int64        `json:"send_time"`
		Origin       int          `json:"origin"`
		MsgType      string       `json:"msgtype"`
		ExternalUser string       `json:"external_userid"`
		Event        EventMessage `json:"event"`
	}

	// 根结构体
	type Response struct {
		ErrCode    int       `json:"errcode"`
		ErrMsg     string    `json:"errmsg"`
		NextCursor string    `json:"next_cursor"`
		MsgList    []Message `json:"msg_list"`
		HasMore    int       `json:"has_more"`
	}

	ret := &Response{}
	_ = json.Unmarshal([]byte(message), ret)

	if ret.ErrCode != 0 {
		return "", true, "", ""
	}

	sort.Slice(ret.MsgList, func(i, j int) bool {
		return ret.MsgList[i].SendTime > ret.MsgList[j].SendTime
	})

	for _, v := range ret.MsgList {
		if v.Event.EventType == "enter_session" && openKfid == v.Event.OpenKfid {
			log.Info("########### 用户进入会话查询消息结果 ", zap.Any("data", utils.ToJsonString(ret)), zap.Any("rsp", message))
			return v.ExternalUser, false, v.Event.WelcomeCode, v.MsgID
		}
		if v.ExternalUser != "" {
			externalUserId = v.ExternalUser
		}
	}

	isSkip = true
	return
}

// 分配客服回话
func AllockSession(orderNo string, kfId string, externalUserid, serviceUserid string, serviceState int) error {
	status := GetServiceState(kfId, externalUserid)
	trace_log.DebugLogger(orderNo, "##########  AllocSession  serviceState:", serviceState, " status:", status, "ExternalUserId：", externalUserid, " serviceUserid: ", serviceUserid, " KfId:", kfId)
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/trans?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		OpenKfid       string `json:"open_kfid"`
		ExternalUserid string `json:"external_userid"`
		ServiceState   int    `json:"service_state"`
		ServicerUserid string `json:"servicer_userid"`
	}{
		OpenKfid:       kfId,
		ExternalUserid: externalUserid,
		ServiceState:   serviceState,
		ServicerUserid: serviceUserid,
	}), nil)

	trace_log.DebugLogger(orderNo, "####### 修改会话状态 AllockSession: rsp:", rsp, " err:", err)
	if err != nil {
		return err
	}

	type Ret struct {
		Code        int    `json:"errcode"`
		Msg         string `json:"errmsg"`
		Invaliduser string `json:"invaliduser"`
	}

	ret := &Ret{}
	err = json.Unmarshal([]byte(rsp), ret)
	if err != nil {
		log.Error("Unmarshal failed ", zap.Any("err", err))
		return fmt.Errorf("%v %v", ret.Code)
	}

	if ret.Code != 0 {
		if ret.Code == 95013 {
			return fmt.Errorf("督导师不在线")
		}
		log.Error("AllockSession failed ", zap.Any("code", ret.Code), zap.Any("rsp", rsp))
		return fmt.Errorf("分配会话失败")
	}

	return nil
}

// CreateLink 创建客服链接
func CreateLink(linkName string, userIds []string) (linkUrl string, err error) {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/externalcontact/customer_acquisition/create_link?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		LinkName string `json:"link_name"`
		Range    struct {
			UserList []string `json:"user_list"`
		} `json:"range"`
		SkipVerify bool `json:"skip_verify"`
	}{
		LinkName: linkName,
		Range: struct {
			UserList []string `json:"user_list"`
		}{
			UserList: userIds,
		},
		SkipVerify: true,
	}), map[string]string{})
	type Ret struct {
		Code int    `json:"errcode"`
		Msg  string `json:"errmsg"`
		Link struct {
			LinkId     string `json:"link_id"`
			LinkName   string `json:"link_name"`
			Url        string `json:"url"`
			CreateTime int64  `json:"create_time"`
		} `json:"link"`
	}

	if err != nil {
		log.Error("CreateLink failed ", zap.Any("err", err), zap.Any("linkName", linkName))
		return "", fmt.Errorf("CreateLink err")
	}

	ret := &Ret{}
	err = json.Unmarshal([]byte(rsp), ret)
	if err != nil {
		log.Error("CreateLink Unmarshal failed ", zap.Any("err", err), zap.Any("linkName", linkName))
		return "", fmt.Errorf("unmarshal err")
	}

	if ret.Code != 0 {
		log.Error("CreateLink  ret.Code  failed", zap.Any("rsp", rsp))
		return "", fmt.Errorf(ret.Msg)
	}
	return ret.Link.Url, nil
}

// SendMessageToUsers 发送应用消息
func SendMessageToUsers(userIds []string, content string) error {
	if len(userIds) <= 0 || content == "" {
		return fmt.Errorf("params illegal")
	}

	toUser := strings.Join(userIds, "|")

	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%v", accessToken)
	rsp, err := request.DoRequest("POST", url, utils.ToJsonString(&struct {
		Touser  string `json:"touser"`
		Msgtype string `json:"msgtype"`
		Agentid int    `json:"agentid"`
		Text    struct {
			Content string `json:"content"`
		} `json:"text"`
	}{
		Touser:  toUser,
		Msgtype: "text",
		Agentid: constant.GetAgentId(config.Conf.Env),
		Text: struct {
			Content string `json:"content"`
		}{
			Content: content,
		},
	}), map[string]string{})

	if err != nil {
		return err
	}

	type Ret struct {
		Code        int    `json:"errcode"`
		Msg         string `json:"errmsg"`
		Invaliduser string `json:"invaliduser"`
	}

	ret := &Ret{}
	err = json.Unmarshal([]byte(rsp), ret)
	if err != nil {
		log.Error("GetUserIdentityByCode failed ", zap.Any("err", err))
		return nil
	}

	if ret.Code != 0 {
		log.Error("GetUserIdentityByCode failed", zap.Any("rsp", string(rsp)))
		return nil
	}

	// 打印推送失败用户
	if ret.Invaliduser != "" {
		log.Error("##### SendMessageToUsers 还有这些玩家没有推送成功", zap.Any("userIds", ret.Invaliduser))
	}
	return nil
}

// GetUserIdentityByCode 根据code获取身份信息
func GetUserIdentityByCode(code string) (string, string, error) {
	accessToken := GetAccessToken()
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=%v&code=%v", accessToken, code)
	rsp, err := request.DoRequest("POST", url, "", map[string]string{})
	type Ret struct {
		Code       int    `json:"errcode"`
		Msg        string `json:"errmsg"`
		UserId     string `json:"userid"`
		UserTicket string `json:"user_ticket"`
	}

	ret := &Ret{}
	err = json.Unmarshal([]byte(rsp), ret)
	if err != nil {
		log.Error("GetUserIdentityByCode failed ", zap.Any("err", err), zap.Any("code", code))
		return "", "", fmt.Errorf("unmarshal err")
	}

	if ret.Code != 0 {
		log.Error("GetUserIdentityByCode failed", zap.Any("rsp", rsp))
		return "", "", fmt.Errorf(ret.Msg)
	}

	if ret.UserId == "" {
		fmt.Println("")
	}
	return ret.UserId, ret.UserTicket, nil
}

// GetAccessToken 获取AccessToken
func GetAccessToken() string {
	if v, ok := utils.GCache.Get("access_token"); ok {
		return v.(string)
	}
	url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%v&corpsecret=%v", constant.GetCorpId(config.Conf.Env), constant.GetSecret(config.Conf.Env))

	rsp, err := request.DoRequest("GET", url, "", map[string]string{})
	type Ret struct {
		Code        int    `json:"errcode"`
		Msg         string `json:"errmsg"`
		Success     bool   `json:"success"`
		AccessToken string `json:"access_token"`
	}

	if err != nil {
		log.Error("GetAccessToken failed ", zap.Any("err", err))
		return ""
	}

	ret := &Ret{}
	err = json.Unmarshal([]byte(rsp), ret)
	if err != nil {
		log.Error("GetAccessToken failed ", zap.Any("rsp", rsp), zap.Any("err", err))
		return ""
	}

	if ret.Code != 0 {
		log.Error("GetAccessToken failed", zap.Any("rsp", string(rsp)))
		return ""
	}

	if ret.AccessToken != "" {
		utils.GCache.Set("access_token", ret.AccessToken, time.Second*3600)
	}

	log.Info("######## GetAccessToken", zap.Any("corpId", constant.GetCorpId(config.Conf.Env)), zap.Any("corpSecret", constant.GetSecret(config.Conf.Env)), zap.Any("accessToken", ret.AccessToken))
	return ret.AccessToken
}

func getString(str, endstr string, start int, msg *string) int {
	end := strings.Index(str, endstr)
	*msg = str[start:end]
	return end + len(endstr)
}

func VerifyURL(w http.ResponseWriter, r *http.Request) {
	//httpstr := `&{GET /?msg_signature=825075c093249d5a60967fe4a613cae93146636b&timestamp=1597998748&nonce=1597483820&echostr=neLB8CftccHiz19tluVb%2BUBnUVMT3xpUMZU8qvDdD17eH8XfEsbPYC%2FkJyPsZOOc6GdsCeu8jSIa2noSJ%2Fez2w%3D%3D HTTP/1.1 1 1 map[Cache-Control:[no-cache] Accept:[*/*] Pragma:[no-cache] User-Agent:[Mozilla/4.0]] 0x86c180 0 [] false 100.108.211.112:8893 map[] map[] <nil> map[] 100.108.79.233:59663 /?msg_signature=825075c093249d5a60967fe4a613cae93146636b&timestamp=1597998748&nonce=1597483820&echostr=neLB8CftccHiz19tluVb%2BUBnUVMT3xpUMZU8qvDdD17eH8XfEsbPYC%2FkJyPsZOOc6GdsCeu8jSIa2noSJ%2Fez2w%3D%3D <nil>}`
	fmt.Println(r, r.Body)
	httpstr := r.URL.RawQuery
	start := strings.Index(httpstr, "msg_signature=")
	start += len("msg_signature=")

	var msg_signature string
	next := getString(httpstr, "&timestamp=", start, &msg_signature)

	var timestamp string
	next = getString(httpstr, "&nonce=", next, &timestamp)

	var nonce string
	next = getString(httpstr, "&echostr=", next, &nonce)

	echostr := httpstr[next:len(httpstr)]

	echostr, _ = url.QueryUnescape(echostr)
	fmt.Println(msg_signature, timestamp, nonce, echostr, next)

	wxcpt := wxbizjsonmsgcrypt.NewWXBizMsgCrypt(constant.GetToken(config.Conf.Env), constant.GetEncodingAeskey(config.Conf.Env), constant.GetCorpId(config.Conf.Env), wxbizjsonmsgcrypt.JsonType)
	echoStr, cryptErr := wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr)
	if nil != cryptErr {
		fmt.Println("verifyUrl fail", cryptErr)
	}
	fmt.Println("verifyUrl success echoStr", string(echoStr))
	fmt.Fprintf(w, string(echoStr))

}

type MsgContent struct {
	ToUserName   string `xml:"ToUserName"`
	FromUsername string `xml:"FromUserName"`
	CreateTime   uint32 `xml:"CreateTime"`
	MsgType      string `xml:"MsgType"`
	Content      string `xml:"Content"`
	Msgid        string `xml:"MsgId"`
	AgentID      uint32 `xml:"AgentID"`
	OpenKfId     string `xml:"OpenKfId"`
	Event        string `xml:"Event"`
	Token        string `xml:"Token"`
}

func MsgHandler(w http.ResponseWriter, r *http.Request) {
	httpstr := r.URL.RawQuery
	start := strings.Index(httpstr, "msg_signature=")
	start += len("msg_signature=")

	var msg_signature string
	next := getString(httpstr, "&timestamp=", start, &msg_signature)

	var timestamp string
	next = getString(httpstr, "&nonce=", next, &timestamp)

	nonce := httpstr[next:len(httpstr)]
	log.Info("### 1 MsgHandler ", zap.Any("msg_signature", msg_signature), zap.Any("timestamp", timestamp), zap.Any("nonce", nonce))

	body, err := ioutil.ReadAll(r.Body)
	log.Info("### 2 MsgHandler ", zap.Any("body", string(body)), zap.Any("err", err))
	//wxcpt := wxbizjsonmsgcrypt.NewWXBizMsgCrypt(token, encodingAeskey, corpid, wxbizjsonmsgcrypt.JsonType)
	wxcpt := wxbizmsgcrypt.NewWXBizMsgCrypt(constant.GetToken(config.Conf.Env), constant.GetEncodingAeskey(config.Conf.Env), constant.GetCorpId(config.Conf.Env), wxbizmsgcrypt.XmlType)
	msg, err_ := wxcpt.DecryptMsg(msg_signature, timestamp, nonce, body)
	fmt.Println(string(msg), err_)
	var msgContent MsgContent
	err = xml.Unmarshal(msg, &msgContent)
	if nil != err {
		log.Info("### 3 MsgHandler Unmarshal fail", zap.Any("err", err))
	} else {
		data := &modelEvent.EnterSession{
			ToUserName: msgContent.ToUserName,
			CreateTime: int64(msgContent.CreateTime),
			MsgType:    msgContent.MsgType,
			Event:      msgContent.Event,
			Token:      msgContent.Token,
			OpenKfId:   msgContent.OpenKfId,
		}
		log.Info("### 4 MsgHandler struct", zap.Any("msgContent", utils.ToJsonString(data)))
		event.Publish(context.Background(), event.MsgReceive, data)

	}

	ToUsername := msgContent.ToUserName
	msgContent.ToUserName = msgContent.FromUsername
	msgContent.FromUsername = ToUsername
	fmt.Println("replaymsg", msgContent)
	replayJson, err := json.Marshal(&msgContent)

	encryptMsg, cryptErr := wxcpt.EncryptMsg(string(replayJson), "1409659589", "1409659589")
	if nil != cryptErr {
		fmt.Println("DecryptMsg fail", cryptErr)
	}

	sEncryptMsg := string(encryptMsg)

	fmt.Println("after encrypt sEncryptMsg: ", sEncryptMsg)
	fmt.Fprintf(w, sEncryptMsg)
}
